#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/file.h>
#include <signal.h>
#include <errno.h>
#include <stdlib.h>
#include <fcntl.h>

#include "skynet_daemon.h"

static int check_pid(const char *pidfile) {
  int pid = 0;
  FILE *f = fopen(pidfile, "r");
  if (f == NULL)
    return 0;
  int n = fscanf(f, "%d", &pid);
  fclose(f);

  if (n != 1 || pid == 0 || pid == getpid()) {
    return 0;
  }

  if (kill(pid, 0) && errno == ESRCH)
    return 0;

  return pid;
}

static int write_pid(const char *pidfile) {
  FILE *f;
  int pid = 0;
  int fd = open(pidfile, O_RDWR | O_CREAT, 0644);
  if (fd == -1) {
    fprintf(stderr, "Can't create pidfile [%s].\n", pidfile);
    return 0;
  }
  f = fdopen(fd, "w+");
  if (f == NULL) {
    fprintf(stderr, "Can't open pidfile [%s].\n", pidfile);
    return 0;
  }

  if (flock(fd, LOCK_EX | LOCK_NB) == -1) {
    int n = fscanf(f, "%d", &pid);
    fclose(f);
    if (n != 1) {
      fprintf(stderr, "Can't lock and read pidfile.\n");
    } else {
      fprintf(stderr, "Can't lock pidfile, lock is held by pid %d.\n", pid);
    }
    return 0;
  }

  pid = getpid();
  if (!fprintf(f, "%d\n", pid)) {
    fprintf(stderr, "Can't write pid.\n");
    close(fd);
    return 0;
  }
  fflush(f);

  return pid;
}

static int redirect_fds() {
  int nfd = open("/dev/null", O_RDWR);
  if (nfd == -1) {
    perror("Unable to open /dev/null: ");
    return -1;
  }
  if (dup2(nfd, 0) < 0) {
    perror("Unable to dup2 stdin(0): ");
    return -1;
  }
  if (dup2(nfd, 1) < 0) {
    perror("Unable to dup2 stdout(1): ");
    return -1;
  }
  if (dup2(nfd, 2) < 0) {
    perror("Unable to dup2 stderr(2): ");
    return -1;
  }

  close(nfd);

  return 0;
}

int daemon_init(const char *pidfile) {
  int pid = check_pid(pidfile);

  if (pid) {
    fprintf(stderr, "Skynet is already running, pid = %d.\n", pid);
    return 1;
  }

#ifdef __APPLE__
  fprintf(stderr, "'daemon' is deprecated: first deprecated in OS X 10.5 , use "
                  "launchd instead.\n");
#else
  if (daemon(1, 1)) {
    fprintf(stderr, "Can't daemonize.\n");
    return 1;
  }
#endif

  pid = write_pid(pidfile);
  if (pid == 0) {
    return 1;
  }

  if (redirect_fds()) {
    return 1;
  }

  return 0;
}

int daemon_exit(const char *pidfile) { return unlink(pidfile); }
